package es.deusto.herotactics.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import jugador.Jugador;
import logica.Accion;
import logica.AccionFinTurno;
import logica.AccionRangoAtaque;
import logica.AccionRangoMovimiento;
import logica.AccionRegenerar;
import logica.ModeloEjecucion;
import logica.Turno;
import mapa.Coordenada;

import organizacion.Equipo;

import unidades.Unidad;
import unidades.UnidadHT;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.widget.CheckBox;
import android.widget.Toast;
import es.deusto.herotactics.ingameandroid.Barra;
import es.deusto.herotactics.ingameandroid.Celda;
import es.deusto.herotactics.ingameandroid.Contexto;
import es.deusto.herotactics.ingameandroid.CurrentAction;
import es.deusto.herotactics.ingameandroid.PantallaAndroid;
import es.deusto.herotactics.ingameandroid.botones.BotonAcabarTurno;
import es.deusto.herotactics.ingameandroid.botones.BotonAtras;
import es.deusto.herotactics.ingameandroid.botones.BotonCancelar;
import es.deusto.herotactics.ingameandroid.botones.BotonGuardar;
import es.deusto.herotactics.ingameandroid.botones.BotonSalir;
import exceptions.PlayerWonException;

/**
 * Clase con métodos de acciones durante la partida
 * 
 * @author Ibai
 *
 */
public class Game {	
	/** ruta en a que está la carpeta del proyecto en la memoria externa*/
	public static final String pathAHT = ModeloEjecucion.getPathAProyecto()+"/Modulos/HeroTactics/";
	
	/**
	 * Acción para mostrar las celdas que están dentro del rango de ataque de la unidad
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 * @param coordActual Coordenada desde la que se ejecuta la acción
	 */
	public static void atacar(PantallaAndroid origen, Coordenada coordActual) {
		Accion accion = new AccionRangoAtaque();
		try {
			ArrayList<Coordenada> rango = (ArrayList<Coordenada>) origen.getModeloHT().accionEnCasilla(coordActual, accion);
			rango = (ArrayList<Coordenada>) rango.clone();
			origen.setCurrentAction(new CurrentAction(origen, accion, rango, coordActual));
		} catch (PlayerWonException e) {
			//no hacer nada, solo puede saltar al atacar
		}
	}
	
	/**
	 * Acción para mostrar las celdas a las que se puede mover la unidad
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 * @param coordActual Coordenada desde la que se ejecuta la acción
	 */
	public static void mover(PantallaAndroid origen, Coordenada coordActual) {
		try {
			Accion accion = new AccionRangoMovimiento();
			ArrayList<Coordenada> rango = (ArrayList<Coordenada>) origen.getModeloHT().accionEnCasilla(coordActual, accion);
			rango = (ArrayList<Coordenada>) rango.clone();
			origen.setCurrentAction(new CurrentAction(origen, accion, rango, coordActual));
		} catch (PlayerWonException e) {
			//no hacer nada, solo puede saltar al atacar
		}
	}
	
	/**
	 * Regenera la unidad seleccionada
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 * @param coordActual Coordenada desde la que se ejecuta la acción
	 */
	public static void regenerar(PantallaAndroid origen, Coordenada coordActual)  {
		try {
			Coordenada coord = (Coordenada) origen.getModeloHT().accionEnCasilla(coordActual, new AccionRegenerar());
			if (coord != null) {
				Celda celda = origen.getCelda(coord);
				celda.actualizarCelda();
				UnidadHT unidad = (UnidadHT) origen.getPartida().getMapa().getContenidoCasilla(coordActual);
				if (unidad != null && !unidad.puedeActuar())
					origen.addCeldaDesactivada(celda);
				Game.actualizarPtosAccion(origen);
				origen.getContexto().destruir();
			}
		} catch (PlayerWonException e) {
			//no hacer nada, solo puede saltar al atacar
		}
	}
	
	/**
	 * Cambia el turno actual 
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 */
	public static void cambiarTurno(PantallaAndroid origen) {
		try {
			origen.getModeloHT().accionEnCasilla(new Coordenada(0,0), new AccionFinTurno());
			asignarTurno(origen, origen.getPartida().getTurno().getJugadorActivo());
			//reinicia los estados de las celdas
			origen.resetCeldasDesactivadas();
		} catch (PlayerWonException e) {
			//no hacer nada, solo puede saltar al atacar
		}
	}
	
	/**
	 * Método para actualizar los campos visuales por los del nuevo turno
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 * @param coordActual Coordenada desde la que se ejecuta la acción
	 */
	public static void asignarTurno(PantallaAndroid origen, Jugador jugador) {
		Barra barra = origen.getBarra();
		if (jugador == origen.getPartida().getJugadorA()) {
			barra.setColorFondo(Color.BLACK);
		} else {
			barra.setColorFondo(Color.WHITE);
		}
		barra.setJugadorTurno(jugador.getNombre());
		Toast.makeText(origen.getContext(), "Turno de jugador "+jugador.getNombre(), Toast.LENGTH_SHORT).show();
	}
	
	/**
	 * Vuelca un equipo al mapa
	 * 
	 * @param unidades capa de unidades del mapa
	 * @param equipo equipo del que volcar las unidades
	 */
	public static void volcarEquipoAMapa(UnidadHT[][] unidades, Equipo equipo) {
		ArrayList<Unidad> uds = (ArrayList<Unidad>) equipo.getUnidades();
		Coordenada coord;
		for (Unidad unidad : uds) {
			UnidadHT udHT = (UnidadHT) unidad;
			coord = udHT.getPosicion();
			unidades[coord.getX()][coord.getY()] = udHT;
		}
	}
	
	/**
	 * Invierte el lado de inicio del equipo (p.e. si está a la izquierda, lo pondrña a la derecha).
	 * No actualiza el mapa, asi que hay que usarlo antes de colocar las unidades en el mapa
	 * 
	 * @param equipo Equipo a mover de zona
	 * @param orientacion Orientación de inversión: Vertical (arriba<->abajo) u Horizontal (izda<->dcha)
	 */
	public static void invertirLadoEquipo(Coordenada coordMax, Equipo equipo, Orientacion orientacion) {
		ArrayList<Unidad> uds = (ArrayList<Unidad>) equipo.getUnidades();
		Coordenada coord;
		if (orientacion == Orientacion.HORIZONTAL) {
			for (Unidad unidad : uds) {
				UnidadHT udHT = (UnidadHT) unidad;
				coord = udHT.getPosicion();
				coord.setX(coordMax.getX()-coord.getX());
			}
		} else if(orientacion == Orientacion.VERTICAL) {
			for (Unidad unidad : uds) {
				UnidadHT udHT = (UnidadHT) unidad;
				coord = udHT.getPosicion();
				coord.setY(coordMax.getY()-coord.getY());
			}
		}
	}
	
	/**
	 * Muestra el menú de opciones durante la partida
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 */
	public static void menuPartida(PantallaAndroid origen) {
		boolean enAccion;
		if (origen.getCurrentAction(true) == null)
			enAccion = false;
		else
			enAccion = true;
		Contexto contexto = origen.getContexto();
		contexto.addBoton(new BotonAtras(origen,"Reanudar"));
		contexto.addBoton(new BotonCancelar(origen, enAccion));
		contexto.addBoton(new BotonAcabarTurno(origen, !enAccion));
		contexto.addBoton(new BotonGuardar(origen));
		contexto.addBoton(new BotonSalir(origen));
		contexto.activar();
	}
	
	/**
	 * Guarda la partida en juego
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 */
	public static void guardar (PantallaAndroid origen) {
		try {
//			ProgressDialog dialog = ProgressDialog.show(origen.getActivity(), "", 
//                    "Guardando, por favor, espere...", true);
//			dialog.show();
			origen.getModeloHT().guardarPartida();
//			dialog.cancel();
			Toast.makeText(origen.getContext(), "Partida guardada", Toast.LENGTH_SHORT).show();
		} catch (Exception e) {
			e.printStackTrace();
			AlertDialog.Builder builder = new AlertDialog.Builder(origen.getContext());
			builder.setMessage("Ha ocurrido un error al guardar la partida")
			.setCancelable(false)
			.setPositiveButton("Aceptar", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int id) {
					dialog.cancel();
				}
			});
			AlertDialog alert = builder.create();
			alert.show();
		}
	}
	
	private static CheckBox guardar;
	
	/**
	 * Mustra una alerta para confirmar que se quiere salir de la partida y da opción
	 * a guardarla
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 */
	public static void salir(final PantallaAndroid origen) {
		guardar = new CheckBox(origen.getContext());
		guardar.setText("Guardar");
		AlertDialog.Builder builder = new AlertDialog.Builder(origen.getContext());
		builder.setMessage("¿Estás seguro de que quieres salir?")
		       .setCancelable(true)
		       .setPositiveButton("Si", new DialogInterface.OnClickListener() {
		           public void onClick(DialogInterface dialog, int id) {
		               origen.getCurrentAction(false).cancel();
		               if (guardar.isChecked()) {
		            	   Game.guardar(origen);
		               }
		               origen.getActivity().finish();
		           }
		       }).setView(guardar)
//		       .setNegativeButton("No", new DialogInterface.OnClickListener() {
//		           public void onClick(DialogInterface dialog, int id) {
//		               origen.getCurrentAction(false).cancel();
//		        	   origen.getActivity().finish();
//		           }
//		           
//		       })
		       .setNeutralButton("No", new DialogInterface.OnClickListener() {
		           public void onClick(DialogInterface dialog, int id) {
		               dialog.cancel();
		           }
		       });
		AlertDialog alert = builder.create();
		alert.show();
	}
	
	/**
	 * Actualiza los puntos de acción del turno
	 * 
	 * @param origen Gestor de pantalla desde el que se está ejecutando la acción
	 */
	public static void actualizarPtosAccion (PantallaAndroid origen) {
		Turno turno = origen.getPartida().getTurno();
		Barra barra = origen.getBarra();
		int ptos = turno.getPuntosAccion();
		if (ptos > barra.getPtosAccion()) {
			origen.resetCeldasDesactivadas();
			Game.asignarTurno(origen, turno.getJugadorActivo());
		} else
			barra.setPtosAccion(ptos);
	}
	
	/**
	 * Instancia todas al unidades que haya especificadas en la ruta de archivo
	 * 
	 * @param filePath Ruta al archivo de texto que contiene las unidades
	 * @return Colección de instancias de las unidades
	 */
	public static ArrayList<UnidadHT> loadUnits(String filePath) {
		ArrayList<UnidadHT> unidades = new ArrayList<UnidadHT>();
		File file = new File(filePath);
		FileInputStream fileInputStream;
		try {
			fileInputStream = new FileInputStream(file);
			InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String line = null;
			do {
				line = bufferedReader.readLine();
				if (line != null) {
					Class<?> clase = Class.forName(line);
					Object objeto = clase.newInstance();
					if (objeto instanceof UnidadHT) 
						unidades.add((UnidadHT) objeto);
					else
						throw new RuntimeException("Wrong class specified in configuration file");
				}
			} while (line != null);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("Unexpected error, check the StackTrace");
		}
		return unidades;
	}
}
